#ifndef XUARTUTILS_H
#define XUARTUTILS_H
/*

 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *

xuart-utils.h - (c) 2012 - Rowan Maclachlan (hippy - dmxout@gmail.com).

*/


/* ----------------------------------------------------------------------------------------
 8 data bits and 2 stop bits at 250000 baud, each bit takes 4 usecs
 and each byte should be 11 bits, for a total of for 44 usec.
 
--- ANSI E1.20 2010
 
Timing Targets:

TX
       			Min	    	Max         
Break -		 	176uS		352uS          
Mark After Break -	12uS		88uS

Inter-slot time <2ms

RX
        	    	Min		Max
Break -		       	176uS		352uS
Mark After Break -  	8uS		88uS

Inter-slot time <2.1ms

(note, the min packet time at 512ch = 22.7mS = max 44hz)

88uS max line turn-around
-----------------------------------------------------------------------   */

#include <netdb.h>


// types of XUART packets
#define XUART_DATA 0
#define XUART_BREAK 1
#define XUART_IDLE 2

// how long is one bit at 250000 baud?
#define BIT_TIME  4         // 4us, our base timing unit

// interrupt processing
#ifndef TX_IRQ_INTERVAL
#define TX_IRQ_INTERVAL  64 // flag the TXIRQ bit every number of slot
// #define TX_IRQ_INTERVAL  192 // works: flag the TXIRQ bit every number of slot

#endif


// experimental
//#define ATOMIC_TX       // test using atomic opcode 0x40xx


// pid of the running xuartctl --server process
int xuartctl_pid;


// prototypes to encode/decode XUART commands
// tx
static inline uint16_t      ch_break(int bits);
static inline uint16_t      ch_idle(int bits);
static inline uint16_t      ch_data(uint8_t data, uint8_t flag_irq);
//rx 
static inline int ch_type(uint16_t data);
static inline int ch_uart(uint16_t data);
static inline unsigned ch_time(uint16_t data);
static inline uint8_t   ch_dat(uint16_t  data);


// yes, I know it's not typical to have functions in a .h file...

//connect
int xuart_connect(char *hostspec, char *args);

// look for a running xuartctl, return a pid
int find_xuartctl(void);




// ================
// TS-7500 XUART OPCODES
// ================


// Signal a break condition
// time in bit-times (4us)
static inline uint16_t   ch_break(int bits) {
  uint16_t   opcode;

  if (bits > 1023) {
    opcode = 0x0C00 + bits / 1024;
  } else {
    opcode = 0x0800 + bits;
  }
  return opcode;
}


// Code for entering a line idle (mark) condition
static inline uint16_t   ch_idle(int bits) {
  uint16_t opcode;

  if (bits > 1023) {
    opcode = 0x1400 + bits / 1024;
  } else {
    opcode = 0x1000 + bits;
  }
  return opcode;
}


// code for encoding the data segments of the packet
// expect data to be between 0x00 and 0xFF
static inline uint16_t   ch_data(uint8_t  data, uint8_t flag_irq) {
  if (flag_irq) return (0x8100 + data); // irq flag and soft stop bit + data
  return (0x100 + data); // soft stop bit + data
}


// type of uart message
// 0 data
// 1 break
// 2 idle
// <> invalid
static inline int ch_type(uint16_t   data) {
  return (data >> 11) & 3;
}

// which uart the data is from
static inline int ch_uart(uint16_t   data) {
  return (data >> 13);
}

// duration of idle or break condition received
static inline unsigned ch_time(uint16_t   data) {
  if (data & 0x400) {
    return (data & 0x3FF) << 6;
  } else {
    return (data & 0x3FF);
  }
}

// actual slot data
static inline uint8_t  ch_dat(uint16_t  data) {
  return (data >> 2) & 0xFF;
}



//connect
int xuart_connect(char *hostspec, char *args);

// look for a running xuartctl, return a pid
int find_xuartctl(void);



// ------- XUART Connect

/* This function is used to connect to a socket and get a socket pointer in
 * return.  It takes two arguments, hostspec and args.  The hostspec must be
 * in the form of hostname:port, if it is NULL it will default to
 * localhost:7350 (the first XUART port).  The args argument is any additional
 * arguments.  In the case of this library, it is required to use at least
 * --mode=raw since the read/write functions provided by this library utilize
 * the raw mode of the XUARTs.  All other options will default to the xuartctl
 * server defaults.
 *
 * This function will return a socket buffer FD
 */

int xuart_connect(char *hostspec, char *args) {
	struct sockaddr_in sa;
	struct hostent *he;
	int sk, r, x, tos = IPTOS_LOWDELAY;
	char *p;
	char buf[1024];
	char cmd[1024];
	struct linger lx;

	/* hostspec must be in form hostname:port */
	if (hostspec) strncpy(buf, hostspec, sizeof(buf));
	else strcpy(buf, "127.0.0.1");
	buf[sizeof(buf) - 1] = 0;

	if (args) {
		snprintf(cmd, sizeof(cmd),
		  "/sbin/xuartctl --server --port=%s %s >/dev/null 2>&1", buf, args);
		r = system(cmd);
		assert (r == 0);
	}

	p = buf;
	while (*p != ':' && *p != 0) p++;
	if (*p == 0) p = "7350";
	else {
		*p = 0;
		p++;
	}

	sa.sin_family = AF_INET;
	he = gethostbyname(buf);
	assert(he != NULL);
	memcpy(&sa.sin_addr, he->h_addr_list[0], he->h_length);
	sa.sin_port = htons(strtoul(p, NULL, 0));
	assert(sa.sin_port != 0);
	sk = socket(PF_INET, SOCK_STREAM, 0);
	assert(sk != -1);
	lx.l_onoff = 1;
	lx.l_linger = 15;
	setsockopt(sk, SOL_SOCKET, SO_LINGER, &lx, sizeof(lx));
	r = connect(sk, (struct sockaddr *)&sa, sizeof(sa));
	setsockopt(sk, IPPROTO_IP, IP_TOS, &tos, 4);
	x = 1;
	setsockopt(sk, IPPROTO_TCP, TCP_NODELAY, &x, 4);
	fcntl(sk, F_SETFL, O_NONBLOCK);
	
	if (r) {
		close(sk);
		return -1;
	} else {
	
	  uint16_t dbuf[2];
      int result = 0;
    //  if (opt_debugv)
       printf("Setting up XUART @ fd %d.\n",sk);

     // set 9-bit mode with rx idle threshold  
   
//    dbuf[0] = 0x190E; // 9 bit, 32 bit-time idle threshold 
//    dbuf[0] = 0x190A; // 9 bit, 16 bit-time idle threshold 
//    dbuf[0] = 0x1906; // 9 bit, 8 bit-time idle threshold 
    dbuf[0] = 0x1902; // 9 bit, 1 bit-time idle threshold 
    dbuf[1] = 0x1F00;
   
    result = write(sk, &dbuf[0], 2*sizeof(uint16_t));
    if (result < 0) {
     printf("Write to XUART error: %d\n",result);
    }
    usleep(1000);

	 return sk;
	}
}





// look for a running xuartctl
int find_xuartctl(void){
// TODO: Check system call returns
    char line[64];
    FILE *cmd = popen("pidof -s xuartctl", "r");
    fgets(line, 64, cmd);
    pid_t pid = strtoul(line, NULL, 10);
    pclose(cmd);
    
    
    if (!pid){
      //  if (opt_debug) 
          printf("Starting xuartctl server... ");
		if (system("chrt -r -p 99 /sbin/xuartctl --server --irq=29") != 0){
		 printf("Failed to start xuartctl server instance!\n");
		 return -1;
		}
		
		
        // check again for pid
        FILE *cmd = popen("pidof -s xuartctl", "r");
        fgets(line, 64, cmd);
        pid_t pid = strtoul(line, NULL, 10);
        pclose(cmd);
        
     //   if (opt_debug) 
          printf("done. [pid=%d].\n",pid);
          return pid;
	}

    return pid;
}





#endif
